package jocl.samples;

import org.jocl.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import static org.jocl.CL.*;
import static org.jocl.CL.clGetDeviceIDs;

/**
 * Created with IntelliJ IDEA.
 * User: Duy Chung
 * Date: 9/15/13
 * Time: 10:49 PM
 * To change this template use File | Settings | File Templates.
 */
public class MultiDeviceComputing {

    private String programSource =
            "__kernel void sampleKernel(__global const float *input,"+
                    "                           __global float *output, " +
                    "                           int size)"+
                    "{"+
                    "    int gid = get_global_id(0);"+
                    "    output[gid] = 0;" +
                    "    for (int i=0; i<size; i++) " +
                    "        output[gid] += input[i];" +
                    "}";

    List<Computation> threads = new ArrayList<Computation>();

    private void compute(int n) {
        // find all devices;
        // The platform and device type that will be used
        final int platformIndex = 0;
        final long deviceType = CL_DEVICE_TYPE_ALL;

        // Enable exceptions and subsequently omit error checks in this sample
        CL.setExceptionsEnabled(true);

        // Obtain the number of platforms
        int numPlatformsArray[] = new int[1];
        clGetPlatformIDs(0, null, numPlatformsArray);
        int numPlatforms = numPlatformsArray[0];

        // Obtain a platform ID
        cl_platform_id platforms[] = new cl_platform_id[numPlatforms];
        clGetPlatformIDs(platforms.length, platforms, null);
        cl_platform_id platform = platforms[platformIndex];
        String platformName = getString(platform, CL_PLATFORM_NAME);
        System.out.println("Using platform "+platformIndex+" of "+
                numPlatforms+": "+platformName);

        // Initialize the context properties
        cl_context_properties contextProperties = new cl_context_properties();
        contextProperties.addProperty(CL_CONTEXT_PLATFORM, platform);

        // Obtain the number of devices for the platform
        int numDevicesArray[] = new int[1];
        clGetDeviceIDs(platform, deviceType, 0, null, numDevicesArray);
        int numDevices = numDevicesArray[0];

        // Obtain a device IDs
        cl_device_id devices[] = new cl_device_id[numDevices];
        clGetDeviceIDs(platform, deviceType, numDevices, devices, null);

        // Execute the kernel on each command queue, and
        // create events for each kernel launch

        for ( int i = 0; i < devices.length; i++) {
            // create context, program, kernel, command for each device
            // Create and build the program and the kernel
            cl_context context = clCreateContext(null, 1, new cl_device_id[]{devices[i]}, null, null, null);
            cl_program program = clCreateProgramWithSource(context, 1, new String[]{ programSource }, null, null);
            clBuildProgram(program, 0, null, null, null, null);
            Computation c = new Computation(n,i, context, program, devices[i]);
            c.setName(getString(devices[i], CL_DEVICE_NAME));
//            c.start();
            threads.add(c);

        }

        long before = System.nanoTime();
        try {
            for (Computation c : threads) {
                c.start();
                c.join();
            }
        } catch ( Exception e ) {
            e.printStackTrace();
        }
        long after = System.nanoTime();
        float totalDurationMs = (after-before)/1e6f;
        System.out.println("Total duration: "+totalDurationMs+"ms");
//        long after = System.nanoTime();
//        float totalDurationMs = (after-before)/1e6f;
//        System.out.println("Total duration: "+totalDurationMs+"ms");
//        Thread[] threads;
//        group.enumerate();
//
//        System.out.print(threads.length);
//        List<Thread> threads = getThreadsFor(Computation)


    }


    Thread[] getGroupThreads( final ThreadGroup group ) {
        if ( group == null )
            throw new NullPointerException( "Null thread group" );
        int nAlloc = group.activeCount( );
        int n = 0;
        Thread[] threads;
        do {
            nAlloc *= 2;
            threads = new Thread[ nAlloc ];
            n = group.enumerate( threads );
        } while ( n == nAlloc );
        return java.util.Arrays.copyOf( threads, n );
    }

    /**
     * Returns the value of the device info parameter with the given name
     *
     * @param device The device
     * @param paramName The parameter name
     * @return The value
     */
    private static String getString(cl_device_id device, int paramName) {
        long size[] = new long[1];
        clGetDeviceInfo(device, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetDeviceInfo(device, paramName,
                buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);

    }

    /**
     * Returns the value of the platform info parameter with the given name
     *
     * @param platform The platform
     * @param paramName The parameter name
     * @return The value
     */
    private static String getString(cl_platform_id platform, int paramName) {
        long size[] = new long[1];
        clGetPlatformInfo(platform, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetPlatformInfo(platform, paramName,
                buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);
    }

    public static void main(String[] args) {
        int n = 170000;

        new MultiDeviceComputing().compute(n);


    }
}
